#region Copyright note and modification history

/*
-----------------------------------------------------------------------------
Copyright (c) 2006 HSI Inc. All Rights Reserved.

PROPRIETARY NOTICE: This software has been provided pursuant to a
License Agreement that contains restrictions on its use. This software
contains valuable trade secrets and proprietary information of
HSI Inc and is protected by Federal copyright law. It may not
be copied or distributed in any form or medium, disclosed to third parties,
or used in any manner that is not provided for in said License Agreement,
except with the prior written authorization of HSI Inc.

-----------------------------------------------------------------------------
$Log: $
-----------------------------------------------------------------------------
*/

#endregion

using System;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.IO;

namespace SimplePushpinGenerator
{
    /// <summary>
    /// Summary description for BalloonCaptionPainter.
    /// </summary>
    public class BalloonCaptionPainter
    {
        private Color _backColor1 = Color.White;
        private Color _backColor2 = Color.WhiteSmoke;
        private int _backGradientAngle = 90;
        private Color _borderColor = Color.Silver;
        private int _borderWidth = 1;
        private bool _centerImage = true;
        private bool _centerText = true;
        private int _cornerRadius = 10;
        private int _margin = 5;
        private int _pointerHeightPercent = 30;
        private int _pointerOffsetPercent = 50;
        private int _pointerWidthPercent = 20;
        private Color _textColor = Color.Navy;
        private Font _captionFont = new Font("Arial", 10);


        [DefaultValue(5)]
        public int Margin
        {
            get { return _margin; }
            set { _margin = value; }
        }

        [DefaultValue(10)]
        public int CornerRadius
        {
            get { return _cornerRadius; }
            set { _cornerRadius = value; }
        }

        [DefaultValue(30)]
        public int PointerHeightPercent
        {
            get { return _pointerHeightPercent; }
            set { _pointerHeightPercent = value; }
        }

        [DefaultValue(20)]
        public int PointerWidthPercent
        {
            get { return _pointerWidthPercent; }
            set { _pointerWidthPercent = value; }
        }

        [DefaultValue(50)]
        public int PointerOffsetPercent
        {
            get { return _pointerOffsetPercent; }
            set { _pointerOffsetPercent = value; }
        }


        [DefaultValue(typeof (Color), "White")]
        public Color BackColor1
        {
            get { return _backColor1; }
            set { _backColor1 = value; }
        }

        [DefaultValue(typeof (Color), "WhiteSmoke")]
        public Color BackColor2
        {
            get { return _backColor2; }
            set { _backColor2 = value; }
        }

        [DefaultValue(typeof (Color), "SlateGray")]
        public Color BorderColor
        {
            get { return _borderColor; }
            set { _borderColor = value; }
        }

        [DefaultValue(1)]
        public int BorderWidth
        {
            get { return _borderWidth; }
            set { _borderWidth = value; }
        }

        [DefaultValue(90)]
        public int BackgroundGradientAngle
        {
            get { return _backGradientAngle; }
            set { _backGradientAngle = value; }
        }

        [DefaultValue(typeof (Color), "Navy")]
        public Color TextColor
        {
            get { return _textColor; }
            set { _textColor = value; }
        }

        [DefaultValue(true)]
        public bool CenterImage
        {
            get { return _centerImage; }
            set { _centerImage = value; }
        }

        [DefaultValue(true)]
        public bool CenterText
        {
            get { return _centerText; }
            set { _centerText = value; }
        }

        public Font CaptionFont
        {
            get { return _captionFont; }
            set { _captionFont = value; }
        }

        public void SaveToFile(string text,Image image,string filePath,ImageFormat imageFormat)
        {
            using (Image img = CreateImage(text,image))
            {
                img.Save(filePath, imageFormat);
            }
        }

        public void SaveToStream(string text,Image image,Stream stream,ImageFormat imageFormat)
        {
            using (Image img = CreateImage(text, image))
            {
                img.Save(stream,imageFormat);
            }
        }

        public Image CreateImage(string text,Image image)
        {
            //We want a max that is plenty big enough. 1000 x 1000 ought to be way more than enough.
            return CreateImage(text, image, 1000, 1000);
        }

        public Image CreateImage(string text, Image image, int maxWidth, int maxHeight)
        {
            using (var bitmap = new Bitmap(maxWidth, maxHeight))
            {
                using (Graphics g = Graphics.FromImage(bitmap))
                {
                    Draw(0, 0, text, image, g);
                    Rectangle bounds = GetBounds(0, 0, text, image, g);
                    var final = new Bitmap(bounds.Width + 1, bounds.Height + 1);
                    using (Graphics fG = Graphics.FromImage(final))
                    {
                        fG.DrawImage(bitmap, 0, 0);
                        return final;
                    }
                }
            }
        }

        public Rectangle GetBounds(int xOffset,int yOffset, string text, Graphics g)
        {
            return GetBounds(xOffset,yOffset, text, null, g);
        }

        public void Draw(int xOffset,int yOffset, string text, Graphics g)
        {
            Draw(xOffset,yOffset, text, null, g);
        }

        public Rectangle GetBounds(int xOffset,int yOffset, string text, Image image, Graphics g)
        {
            Rectangle bounds = GetInnerBounds(xOffset,yOffset, text, image, g);
            GraphicsPath path = GetBalloonPath(bounds);
            return Rectangle.Round(path.GetBounds());
        }

        public void Draw(int xOffset,int yOffset, string text, Image image, Graphics g)
        {
            g.SmoothingMode = SmoothingMode.AntiAlias;

            Rectangle innerBounds = GetInnerBounds(xOffset,yOffset, text, image, g);

            GraphicsPath path = GetBalloonPath(innerBounds);

            DrawBalloonBackground(path, g);
            DrawImage(image, text, innerBounds, g);
            DrawText(text, image, innerBounds, g);
            DrawBalloonBorder(path, g);
        }


        protected virtual void DrawBalloonBackground(GraphicsPath path, Graphics g)
        {
            using (
                var brush = new LinearGradientBrush(path.GetBounds(), BackColor1, BackColor2, BackgroundGradientAngle))
            {
                g.FillPath(brush, path);
            }
        }

        protected virtual void DrawImage(Image image, string text, Rectangle innerBounds, Graphics g)
        {
            if (image != null)
            {
                var imageRect = new Rectangle(innerBounds.X + Margin, innerBounds.Y + Margin, image.Width, image.Height);

                if (CenterImage)
                {
                    imageRect.Y = innerBounds.Y + (innerBounds.Height/2) - (image.Height/2);
                }
                g.DrawImage(image, imageRect);
            }
        }

        protected virtual void DrawText(string text, Image image, Rectangle innerBounds, Graphics g)
        {
            Size textSize = Size.Round(g.MeasureString(text, CaptionFont));
            var textRect = new Rectangle(innerBounds.X + Margin, innerBounds.Y + Margin, textSize.Width, textSize.Height);

            if (image != null)
                textRect.X += image.Width + Margin;

            if (CenterText)
            {
                textRect.Y = innerBounds.Y + (innerBounds.Height/2) - (textSize.Height/2);
            }

            using (var brush = new SolidBrush(TextColor))
            {
                g.DrawString(text, CaptionFont, brush, textRect.X, textRect.Y);
            }
        }

        protected virtual void DrawBalloonBorder(GraphicsPath path, Graphics g)
        {
            using (var pen = new Pen(BorderColor, BorderWidth))
            {
                g.DrawPath(pen, path);
            }
        }

        private Rectangle GetInnerBounds(int leftOffset,int topOffset, string text, Image image, Graphics g)
        {
            Size textSize = (text != null) ? Size.Round(g.MeasureString(text, CaptionFont)) : Size.Empty;
            Size imageSize = (image != null) ? image.Size : Size.Empty;

            var bounds = new Rectangle(0, 0, Margin*2, Margin*2);


            //if there is text and image, we need to add the space between
            //the image and the text
            if (textSize.Width > 0 && imageSize.Width > 0)
                bounds.Width += Margin;

            bounds.Width += textSize.Width;
            bounds.Height += textSize.Height;

            bounds.Width += imageSize.Width;

            if (bounds.Height < imageSize.Height + Margin*2)
                bounds.Height = imageSize.Height + Margin*2;

            bounds.X = leftOffset;
            bounds.Y = topOffset;
            return bounds;
        }

        private GraphicsPath GetBalloonPath(Rectangle bounds)
        {
            int pointerOffset = Convert.ToInt32(bounds.Width*(PointerOffsetPercent/100.0f));
            int pointerHeight = Convert.ToInt32(bounds.Height*(PointerHeightPercent/100.0f));
            int pointerWidth = Convert.ToInt32(bounds.Width*(PointerWidthPercent/100f));
            return RectangleExtensions.GetCaptionBalloon(bounds, CornerRadius,
                                                         new Point(bounds.X + (bounds.Width/2),
                                                                   bounds.Bottom + pointerHeight), pointerOffset,pointerWidth);
        }


    }
}